"""
cProfile 可视化示例
演示如何将性能分析结果可视化
"""
import cProfile
import pstats
import io
from typing import Dict, List, Tuple


# ============================================================================
# 方法 1: 生成文本树状图
# ============================================================================
def generate_call_tree(stats: pstats.Stats, max_depth: int = 3) -> str:
    """生成调用树文本可视化"""
    
    def format_time(t: float) -> str:
        """格式化时间"""
        if t < 0.001:
            return f"{t*1000000:.1f}μs"
        elif t < 1:
            return f"{t*1000:.1f}ms"
        else:
            return f"{t:.3f}s"
    
    def build_tree(func, depth=0, visited=None):
        """递归构建调用树"""
        if visited is None:
            visited = set()
        
        if depth > max_depth or func in visited:
            return []
        
        visited.add(func)
        
        lines = []
        indent = "  " * depth
        
        if func in stats.stats:
            cc, nc, tt, ct, callers = stats.stats[func]
            func_name = f"{func[2]}"
            time_str = format_time(ct)
            calls_str = f"{nc}次"
            
            # 生成树节点
            prefix = "└─ " if depth > 0 else ""
            lines.append(f"{indent}{prefix}{func_name} [{time_str}, {calls_str}]")
            
            # 获取被调用的函数
            callees = {}
            for (caller, callee), (cc, nc, tt, ct) in stats.all_callees.items():
                if caller == func:
                    callees[callee] = ct
            
            # 按时间排序
            sorted_callees = sorted(callees.items(), key=lambda x: x[1], reverse=True)
            
            # 递归处理子调用（只取前5个）
            for callee, _ in sorted_callees[:5]:
                lines.extend(build_tree(callee, depth + 1, visited.copy()))
        
        return lines
    
    # 找到根函数（累计时间最长的）
    root_func = max(stats.stats.items(), key=lambda x: x[1][3])[0]
    
    tree_lines = ["调用树视图:", "=" * 60]
    tree_lines.extend(build_tree(root_func))
    
    return '\n'.join(tree_lines)


# ============================================================================
# 方法 2: 生成表格视图
# ============================================================================
def generate_table_view(stats: pstats.Stats, limit: int = 20) -> str:
    """生成表格视图"""
    
    # 提取数据
    data = []
    for func, (cc, nc, tt, ct, callers) in stats.stats.items():
        data.append({
            'function': f"{func[2]}",
            'file': func[0],
            'line': func[1],
            'ncalls': nc,
            'tottime': tt,
            'cumtime': ct,
            'percall': ct / nc if nc > 0 else 0
        })
    
    # 按累计时间排序
    data.sort(key=lambda x: x['cumtime'], reverse=True)
    data = data[:limit]
    
    # 生成表格
    lines = ["\n表格视图:", "=" * 100]
    
    # 表头
    header = f"{'函数':<30} {'文件':<25} {'行号':<6} {'调用次数':<10} {'总时间':<12} {'累计时间':<12} {'单次':<10}"
    lines.append(header)
    lines.append("-" * 100)
    
    # 数据行
    for row in data:
        line = (f"{row['function']:<30} "
                f"{row['file'][-25:]:<25} "
                f"{row['line']:<6} "
                f"{row['ncalls']:<10} "
                f"{row['tottime']:<12.6f} "
                f"{row['cumtime']:<12.6f} "
                f"{row['percall']:<10.6f}")
        lines.append(line)
    
    return '\n'.join(lines)


# ============================================================================
# 方法 3: 生成火焰图数据（文本模拟）
# ============================================================================
def generate_flame_graph_data(stats: pstats.Stats) -> str:
    """生成火焰图数据（文本模拟）"""
    
    def get_bar(percentage: float, width: int = 50) -> str:
        """生成进度条"""
        filled = int(percentage * width)
        return '█' * filled + '░' * (width - filled)
    
    # 提取数据
    data = []
    total_time = stats.total_tt
    
    for func, (cc, nc, tt, ct, callers) in stats.stats.items():
        if ct > 0.001:  # 过滤掉很小的时间
            percentage = ct / total_time if total_time > 0 else 0
            data.append({
                'function': func[2],
                'cumtime': ct,
                'percentage': percentage
            })
    
    # 按时间排序
    data.sort(key=lambda x: x['cumtime'], reverse=True)
    
    # 生成火焰图
    lines = ["\n火焰图视图（模拟）:", "=" * 80]
    lines.append(f"总时间: {total_time:.6f}s\n")
    
    for item in data[:15]:
        bar = get_bar(item['percentage'])
        time_str = f"{item['cumtime']:.6f}s"
        pct_str = f"{item['percentage']*100:.1f}%"
        lines.append(f"{item['function']:<30} {bar} {pct_str:>7} ({time_str})")
    
    return '\n'.join(lines)


# ============================================================================
# 方法 4: 生成时间线视图
# ============================================================================
def generate_timeline_view(profilers: List[Tuple[str, cProfile.Profile]]) -> str:
    """生成时间线视图（比较多个分析结果）"""
    
    lines = ["\n时间线视图:", "=" * 80]
    
    max_time = 0
    for name, profiler in profilers:
        stats = pstats.Stats(profiler)
        max_time = max(max_time, stats.total_tt)
    
    for name, profiler in profilers:
        stats = pstats.Stats(profiler)
        time = stats.total_tt
        percentage = time / max_time if max_time > 0 else 0
        
        bar_width = int(percentage * 60)
        bar = '█' * bar_width
        
        lines.append(f"{name:<20} {bar} {time:.6f}s ({percentage*100:.1f}%)")
    
    return '\n'.join(lines)


# ============================================================================
# 方法 5: 生成统计摘要
# ============================================================================
def generate_summary(stats: pstats.Stats) -> str:
    """生成统计摘要"""
    
    # 收集统计信息
    total_calls = stats.total_calls
    total_time = stats.total_tt
    num_functions = len(stats.stats)
    
    # 找出最慢的函数
    slowest = max(stats.stats.items(), key=lambda x: x[1][3])
    slowest_func = slowest[0]
    slowest_time = slowest[1][3]
    
    # 找出调用最多的函数
    most_called = max(stats.stats.items(), key=lambda x: x[1][1])
    most_called_func = most_called[0]
    most_called_count = most_called[1][1]
    
    lines = [
        "\n性能分析摘要:",
        "=" * 60,
        f"总函数数: {num_functions}",
        f"总调用次数: {total_calls}",
        f"总执行时间: {total_time:.6f}秒",
        f"平均每次调用: {total_time/total_calls*1000:.3f}毫秒",
        "",
        "最慢的函数:",
        f"  {slowest_func[2]} ({slowest_func[0]}:{slowest_func[1]})",
        f"  累计时间: {slowest_time:.6f}秒",
        "",
        "调用最多的函数:",
        f"  {most_called_func[2]} ({most_called_func[0]}:{most_called_func[1]})",
        f"  调用次数: {most_called_count}",
    ]
    
    return '\n'.join(lines)


# ============================================================================
# 示例函数
# ============================================================================
def example_task_1():
    """示例任务1 - 列表处理"""
    data = [i ** 2 for i in range(10000)]
    return sum(data)


def example_task_2():
    """示例任务2 - 字符串处理"""
    text = "test " * 10000
    return text.upper().split()


def example_task_3():
    """示例任务3 - 嵌套循环"""
    result = 0
    for i in range(100):
        for j in range(100):
            result += i * j
    return result


# ============================================================================
# 完整示例
# ============================================================================
def demo_all_visualizations():
    """演示所有可视化方法"""
    print("cProfile 可视化示例")
    print("=" * 80)
    
    # 创建分析器
    profiler = cProfile.Profile()
    profiler.enable()
    
    # 执行任务
    example_task_1()
    example_task_2()
    example_task_3()
    
    profiler.disable()
    
    # 创建统计对象
    stats = pstats.Stats(profiler)
    stats.sort_stats('cumulative')
    
    # 1. 统计摘要
    print(generate_summary(stats))
    
    # 2. 表格视图
    print(generate_table_view(stats, limit=15))
    
    # 3. 调用树
    # 注意：需要 stats 有 all_callees 属性
    try:
        stats.calc_callees()
        print(generate_call_tree(stats, max_depth=2))
    except AttributeError:
        print("\n调用树视图需要计算 callees")
    
    # 4. 火焰图（模拟）
    print(generate_flame_graph_data(stats))
    
    # 5. 时间线比较
    profilers = []
    
    # 任务1
    p1 = cProfile.Profile()
    p1.enable()
    example_task_1()
    p1.disable()
    profilers.append(("列表处理", p1))
    
    # 任务2
    p2 = cProfile.Profile()
    p2.enable()
    example_task_2()
    p2.disable()
    profilers.append(("字符串处理", p2))
    
    # 任务3
    p3 = cProfile.Profile()
    p3.enable()
    example_task_3()
    p3.disable()
    profilers.append(("嵌套循环", p3))
    
    print(generate_timeline_view(profilers))


# ============================================================================
# 导出为 HTML 报告
# ============================================================================
def export_html_report(stats: pstats.Stats, filename: str = 'profile_report.html'):
    """导出为 HTML 报告"""
    
    html = f"""
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>性能分析报告</title>
    <style>
        body {{ font-family: Arial, sans-serif; margin: 20px; }}
        h1 {{ color: #333; }}
        table {{ border-collapse: collapse; width: 100%; margin: 20px 0; }}
        th, td {{ border: 1px solid #ddd; padding: 8px; text-align: left; }}
        th {{ background-color: #4CAF50; color: white; }}
        tr:nth-child(even) {{ background-color: #f2f2f2; }}
        .summary {{ background-color: #e7f3fe; padding: 15px; border-left: 6px solid #2196F3; }}
    </style>
</head>
<body>
    <h1>性能分析报告</h1>
    
    <div class="summary">
        <h2>摘要</h2>
        <p>总调用次数: {stats.total_calls}</p>
        <p>总执行时间: {stats.total_tt:.6f}秒</p>
        <p>函数数量: {len(stats.stats)}</p>
    </div>
    
    <h2>详细统计</h2>
    <table>
        <tr>
            <th>函数</th>
            <th>文件</th>
            <th>行号</th>
            <th>调用次数</th>
            <th>总时间(s)</th>
            <th>累计时间(s)</th>
            <th>单次时间(s)</th>
        </tr>
"""
    
    # 添加数据行
    sorted_stats = sorted(stats.stats.items(), key=lambda x: x[1][3], reverse=True)
    for func, (cc, nc, tt, ct, callers) in sorted_stats[:30]:
        percall = ct / nc if nc > 0 else 0
        html += f"""
        <tr>
            <td>{func[2]}</td>
            <td>{func[0]}</td>
            <td>{func[1]}</td>
            <td>{nc}</td>
            <td>{tt:.6f}</td>
            <td>{ct:.6f}</td>
            <td>{percall:.6f}</td>
        </tr>
"""
    
    html += """
    </table>
</body>
</html>
"""
    
    with open(filename, 'w', encoding='utf-8') as f:
        f.write(html)
    
    print(f"\nHTML 报告已导出到: {filename}")


def demo_html_export():
    """演示 HTML 导出"""
    print("\n" + "=" * 60)
    print("HTML 报告导出示例")
    print("=" * 60)
    
    profiler = cProfile.Profile()
    profiler.enable()
    
    example_task_1()
    example_task_2()
    example_task_3()
    
    profiler.disable()
    
    stats = pstats.Stats(profiler)
    export_html_report(stats)


# ============================================================================
# 主函数
# ============================================================================
if __name__ == '__main__':
    demo_all_visualizations()
    demo_html_export()
